package com.ceair.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ceair.entity.ProcessDefinition;
import com.ceair.entity.request.*;
import com.ceair.entity.result.PageResult;
import com.ceair.entity.result.Result;
import com.ceair.entity.vo.ProcessDefinitionVO;
import com.ceair.exception.BusinessException;
import com.ceair.service.IProcessDefinitionService;
import com.ceair.utils.ProcessDefinitionStructMapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * @author wangbaohai
 * @ClassName ProcessDefinitionController
 * @description: 流程定义管理相关接口
 * @date 2025年04月12日
 * @version: 1.0.0
 */
@RestController
@RequestMapping("/api/v1/definition")
@RequiredArgsConstructor
@Slf4j
@Tag(name = "流程定义管理", description = "流程定义管理相关接口")
public class ProcessDefinitionController {

    private final IProcessDefinitionService processDefinitionService;

    /**
     * 分页查询流程定义信息。
     *
     * @param req 流程定义查询请求对象，包含分页和筛选条件等信息，不能为空。
     * @return 包含流程定义视图对象列表的分页结果。如果查询或转换失败，则返回错误信息。
     *
     * 功能描述：
     * 1. 调用服务层方法查询流程定义信息，并返回分页结果。
     * 2. 将查询结果中的实体对象转换为视图对象（VO）列表。
     * 3. 如果转换过程中出现异常，记录错误日志并过滤掉转换失败的对象。
     * 4. 返回封装了视图对象列表和总记录数的分页结果对象。
     * 5. 如果查询过程中发生异常，记录错误日志并返回错误信息。
     */
    @PreAuthorize("hasAnyAuthority('/api/v1/definition/page')")
    @Parameter(name = "req", description = "流程定义查询请求对象", required = true)
    @Operation(summary = "分页查询流程定义")
    @PostMapping("/page")
    public PageResult<List<ProcessDefinitionVO>> queryProcessDefinitionWithPage(@RequestBody QueryProcessDefinitionReq req) {
        try {
            // 调用服务层方法查询流程定义，并返回封装的分页结果
            Page<ProcessDefinition> page = processDefinitionService.queryProcessDefinitionWithPage(req);

            // 将查询结果中的记录转换为视图对象（VO）列表
            List<ProcessDefinitionVO> list =
                    Optional.ofNullable(page.getRecords()).orElse(Collections.emptyList()).parallelStream().map(record -> {
                try {
                    // 使用映射器将实体对象转换为视图对象
                    return ProcessDefinitionStructMapper.INSTANCE.toVO(record);
                } catch (Exception e) {
                    // 记录转换失败的错误信息
                    log.error("转换流程定义信息失败 具体原因为 : {}", e.getMessage());
                    return null;
                }
            }).filter(Objects::nonNull).toList();

            // 返回包含转换后列表和列表大小的PageResult对象
            return PageResult.success(list, page.getTotal());
        } catch (Exception e) {
            // 记录查询失败的错误信息，并返回错误结果
            log.error("查询流程定义信息失败 具体原因为 : {}", e.getMessage());
            return PageResult.error("查询流程定义信息失败：" + e.getMessage());
        }
    }

    /**
     * 新增或更新流程定义。
     * <p>
     * 该方法通过调用服务层的保存逻辑，处理流程定义的新增或更新操作。
     * 如果操作成功，则返回封装的成功响应；如果发生异常，则记录错误日志并返回错误响应。
     *
     * @param req 流程定义新增或更新请求对象，包含必要的流程定义信息。
     *            该参数为必需项，不能为空。
     * @return 返回一个Result对象，包含操作结果：
     * - 如果操作成功，返回封装的ProcessDefinitionVO对象；
     * - 如果操作失败，返回错误信息及原因。
     */
    @PreAuthorize("hasAnyAuthority('/api/v1/definition/save')") // 限制访问权限，仅允许拥有'/api/v1/definition/save'权限的用户调用该方法
    @Parameter(name = "req", description = "流程定义新增或更新请求对象", required = true) // 定义接口参数的描述信息，用于生成API文档
    @Operation(summary = "新增或更新流程定义") // 定义接口的摘要信息，用于生成API文档
    @PostMapping("/save") // 映射HTTP POST请求到该方法，URL路径为"/api/v1/definition/save"
    public Result<ProcessDefinitionVO> save(@RequestBody ProcessDefinitionInOrUpReq req) {
        // 初始化流程定义视图对象
        ProcessDefinitionVO processDefinitionVO;

        // 调用服务层方法保存流程定义，并将结果封装为成功响应返回
        try {
            processDefinitionVO = processDefinitionService.save(req);
        } catch (IllegalArgumentException e) {
            // 捕获参数校验或业务逻辑异常，记录错误日志并返回错误响应
            log.error("新增或更新流程定义失败，参数异常: {}", e.getMessage(), e);
            return Result.error("新增或更新流程定义失败，原因：" + e.getMessage());
        } catch (Exception e) {
            // 捕获其他异常，记录错误日志并返回错误响应
            log.error("新增或更新流程定义失败 具体原因为 : {}", e.getMessage());
            return Result.error("新增或更新流程定义失败,失败原因：" + e.getMessage());
        }

        // 返回操作成功的响应结果
        return Result.success(processDefinitionVO);
    }

    /**
     * 查询流程定义XML的接口方法。
     *
     * 该方法通过接收一个查询请求对象，调用服务层获取流程定义的XML内容，并返回封装的结果。
     *
     * @param queryOneProcessDefinitionReq 查询流程定义XML请求对象，包含查询所需的参数信息，不能为空。
     *                                     该对象通过HTTP请求体传递。
     * @return 返回一个Result对象，其中包含流程定义的XML字符串。
     *         如果操作成功，Result对象的状态为成功，并携带XML数据；
     *         如果失败，Result对象会包含相应的错误信息。
     */
    @PreAuthorize("hasAnyAuthority('/api/v1/definition/getProcessDefinitionXml')")
    @Parameter(name="queryOneProcessDefinitionReq", description = "查询流程定义XML请求对象", required = true)
    @Operation(summary = "查询流程定义XML")
    @PostMapping("/getProcessDefinitionXml")
    public Result<String> getProcessDefinitionXml(@RequestBody QueryOneProcessDefinitionReq queryOneProcessDefinitionReq) {
        // 调用服务层方法获取流程定义的XML内容
        return Result.success(processDefinitionService.getProcessDefinitionXml(queryOneProcessDefinitionReq));
    }

    /**
     * 保存流程定义XML的接口方法。
     *
     * <p>该方法通过接收一个保存流程定义XML的请求对象，调用服务层逻辑保存流程定义XML文件。
     * 方法使用了权限控制注解{@code @PreAuthorize}，确保只有具备指定权限的用户可以访问。
     * 同时，方法通过{@code @Operation}和{@code @Parameter}注解提供了接口的描述信息，便于生成API文档。</p>
     *
     * @param saveProcessDefinitionXmlReq 保存流程定义XML请求对象，包含保存流程定义所需的数据。
     *                                    该参数为必需参数，且通过{@code @RequestBody}注解绑定到HTTP请求体。
     * @return {@link Result<Boolean>} 返回操作结果的封装对象：
     *         - 如果保存成功，返回{@code Result.success(true)}；
     *         - 如果发生已知业务异常，返回错误信息及具体原因；
     *         - 如果发生未知异常，返回通用错误信息及具体原因。
     */
    @PreAuthorize("hasAnyAuthority('/api/v1/definition/saveProcessDefinitionXml')")
    @Parameter(name="saveProcessDefinitionXmlReq", description = "保存流程定义XML请求对象", required = true)
    @Operation(summary = "保存流程定义XML")
    @PostMapping("/saveProcessDefinitionXml")
    public Result<Boolean> saveProcessDefinitionXml(@RequestBody SaveProcessDefinitionXmlReq saveProcessDefinitionXmlReq) {
        try {
            // 调用服务层方法保存流程定义XML，并返回成功结果
            return Result.success(processDefinitionService.saveBpmnXml(saveProcessDefinitionXmlReq));
        } catch (BusinessException e) {
            // 捕获业务逻辑异常，返回具体的错误信息
            return Result.error("保存流程定义失败，发生已知业务异常，具体原因：" + e);
        } catch (Exception e) {
            // 捕获其他未知异常，返回通用错误信息
            return Result.error("保存流程定义失败，发生未知知业务异常，具体原因：" + e);
        }
    }

    /**
     * 部署流程定义XML的接口方法。
     *
     * 该方法通过接收一个部署流程定义XML的请求对象，调用服务层方法完成流程定义的部署。
     * 如果部署过程中发生异常，会根据异常类型返回具体的错误信息。
     *
     * @param deployProcessDefinitionXmlReq 部署流程定义XML请求对象，包含部署所需的必要信息（必填）。
     * @return 返回一个Result对象，包含布尔值表示部署是否成功。
     *         - 如果成功，返回Result.success(true)。
     *         - 如果失败，返回Result.error()，并附带具体的错误信息。
     */
    @PreAuthorize("hasAnyAuthority('/api/v1/definition/deployProcessDefinitionXml')")
    @Parameter(name="deployProcessDefinitionXmlReq", description = "部署流程定义XML请求对象", required = true)
    @Operation(summary = "部署流程定义XML")
    @PostMapping("/deployProcessDefinitionXml")
    public Result<Boolean> deployProcessDefinitionXml(@RequestBody DeployProcessDefinitionXmlReq deployProcessDefinitionXmlReq) {
        try {
            // 调用服务层方法部署流程定义XML，并返回成功结果
            return Result.success(processDefinitionService.deployBpmnXml(deployProcessDefinitionXmlReq));
        } catch (BusinessException e) {
            // 捕获业务逻辑异常，返回具体的错误信息
            return Result.error("部署流程定义失败，发生已知业务异常，具体原因：" + e);
        } catch (Exception e) {
            // 捕获其他未知异常，返回通用错误信息
            return Result.error("部署流程定义失败，发生未知业务异常，具体原因：" + e);
        }
    }

}
